		//chatServer.c
		//Ashish Tomar
		//2009/1/27
		//Network Security -- HW0
		
		//xxxxxx
		//see list memeory allocation
		//see char * and line input
		//xxxxxx
		#include "md5wrapper.h"
		# include <openssl/evp.h>
		#include <stdio.h>
		#include <string.h>
		#include <sys/types.h>
		#include <sys/socket.h>
		#include <netdb.h>
		#include <arpa/inet.h>
		#include <stdlib.h>
		#include <unistd.h>
		#include <errno.h>
		#include <netinet/in.h>
		#include <sys/wait.h>
		#include <signal.h>
		#include <pthread.h>
		#include <stdio.h>
		#include<ctype.h>
		#include <pwd.h>
		#include <time.h>
		#include <dirent.h>
		#include <sys/stat.h>
        #include <stdlib.h>
		#include <iostream>
		using namespace std;



		#define PORT "3492"  // the port users will be connecting to

		#define BACKLOG 10     // how many pending connections queue will hold
		#define MAXDATASIZE 2048 


		pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
		
		void *threaded_server( void *fdess);

		struct Clientss
		{
			char name[MAXDATASIZE];
			char pass[MAXDATASIZE];
			 char passkey[MAXDATASIZE];
			unsigned char *passphrase;
			
			
			int fdno;
			int test;
			int flag;
		};

	typedef struct Clientss Client;

	
		struct ClientDatabase
		{
			char *name;
			char *pass;
			char *key;
		struct ClientDatabase *next;
		} *head;
	typedef struct ClientDatabase item;


std::string getmd5(char *);

char * search(item *q,char *nam,char *passs)
{ 
	char *ret=" ";
	if(q==NULL)
{ 
	printf("\nNo Database\n");
}
else
	{
//printf("\n");
			while(q!=NULL)

			{ 
//			printf("%s:",q->arr);
				if(!(strcmp(q->name,nam)))
				{
if(!(strcmp(q->pass,passs)))
					{
return q->key;
					}
				}
			q=q->next;

			}
			//printf("\n");
	}

return ret;
}



int print_hex(unsigned char *buf, int len)
{
	int i;
	int n;

	for (i = 0, n = 0; i < len; i++) {
		if (n > 7) {
			printf("\n");
			n = 0;
		}
		printf("0x%02x, ", buf[i]);
		n++;
	}
	printf("\n");

	return (0);
}


item* append(item **q,char *var,char *passs,char *keyy)
{ 
						item *temp,*r, *ret;

					temp = *q;

					if(*q==NULL)

					{ temp = (item *)malloc(sizeof(item));

					temp->name=var;
					temp->pass=passs;
					temp->key=keyy;

					temp->next=NULL;

					*q=temp;
					//q=&temp;
					ret =*q;

					}

					else

					{ 
						temp = *q;
							ret= *q;
					while(temp->next !=NULL)

					{ temp=temp->next;

					}

					r = (item *)malloc(sizeof(item));

					r->name=var;
					r->pass=passs;
					r->key=keyy;

					r->next=NULL;

					temp->next=r;

					}

return ret;
}










void del (item *p,char *num)
 {
item *temp, *m, *temp2;
temp=p;
      while(temp!=NULL)
    {
      if(!(strcmp(temp->name, num)))
       {
           if(temp==p)
           {
              p=temp->next;
			  head=p;
			  temp2 =temp;
			  temp=temp->next;
              free(temp2);
              
           }
           else
         {
           m->next=temp->next;
		   temp2 =temp;
		    temp=temp->next;
           free(temp2);
           
         }
      }else
        {
           m=temp;
          temp= temp->next;
        }

}
    
}




	Client *list;
	int num=0;
	int numlist=0;

	int curnum=0;


		void sigchld_handler(int s)
		{
			while(waitpid(-1, NULL, WNOHANG) > 0);
		}


		void *get_in_addr(struct sockaddr *sa)
		{
			if (sa->sa_family == AF_INET) {
				return &(((struct sockaddr_in*)sa)->sin_addr);
			}

			return &(((struct sockaddr_in6*)sa)->sin6_addr);
		}

void addlist()
{
	

							head=append(&head,"test1","test1","no1");
							

							head=append(&head,"test2","test2","no2");
							

							head=append(&head,"test3","test3","no3");
							head=append(&head,"test4","test4","no4");

}

pthread_t thread1[20];
		int main(int argc, char *argv[])
		{
							int sockfd, new_fd,numbytes;  // listen on sock_fd, new connection on new_fd
							struct addrinfo hints, *servinfo, *p;
							struct sockaddr_storage their_addr; // connector's address information
							socklen_t sin_size;
							struct sigaction sa;
							char buf[MAXDATASIZE];
							int yes=1;
							char s[INET6_ADDRSTRLEN];
							
							unsigned char key_material[256];

	


							int rv;

							memset(&hints, 0, sizeof hints);
							hints.ai_family = AF_UNSPEC;
							hints.ai_socktype = SOCK_STREAM;
							hints.ai_flags = AI_PASSIVE; // use my IP
addlist();


							if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) == -1) 
							{
								printf( "getaddrinfo: \n");
								return 1;
							}
						for(p = servinfo; p != NULL; p = p->ai_next)
							{
								if ((sockfd = socket(p->ai_family, p->ai_socktype,p->ai_protocol)) == -1) 
								{
									perror("server: socket");
									continue;
								}

								if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,sizeof(int)) == -1) 
								{
									perror("setsockopt");
									exit(1);
								}

								if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1)
								{
									close(sockfd);
									perror("server: bind");
									continue;
								}

								break;
							}

							if (p == NULL) 
							{
								fprintf(stderr, "server: failed to bind\n");
								return 2;
							}

							freeaddrinfo(servinfo); // all done with this structure

							if (listen(sockfd, BACKLOG) == -1)
							{
								perror("listen");
								exit(1);
							}

							sa.sa_handler = sigchld_handler; // reap all dead processes
							sigemptyset(&sa.sa_mask);
							sa.sa_flags = SA_RESTART;
							if (sigaction(SIGCHLD, &sa, NULL) == -1)
							{
								perror("sigaction");
								exit(1);
							}

							printf("server: waiting for connections...\n");

							int no=0;
							int th=0,iret2;

							while(1) 
								{  // main accept() loop
														int t=0;
														sin_size = sizeof their_addr;
														new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
														int *pp=&new_fd;
														if (new_fd == -1) 
														{
																	perror("accept");
																	continue;
														}

														inet_ntop(their_addr.ss_family,
														get_in_addr((struct sockaddr *)&their_addr),s, sizeof s);
														printf("server: got connection from %s\n", s);

														//XXXXX---need to use realloc for struct list
														list= (Client *)realloc((Client *)list,  sizeof(Client));
														//list=(Client *)malloc(5* sizeof(Client));
														
														numlist=numlist+1;
														no=numlist-1;
														curnum=numlist-1;
														list[no].fdno=new_fd;
														//curnum=no;
														
														

														//thread1= (pthread_t *)realloc((pthread_t *)thread1, sizeof(pthread_t));
														
														iret2 = pthread_create( &thread1[no], NULL, &threaded_server, (void *)new_fd);
														num=no;
														no++;
		
							}


								
								return 0;
		}





	void *threaded_server( void * ne)
	{
			int cur=curnum;
			int nefd= (int)ne;
			char buf[MAXDATASIZE];
		
				char bufhold[2*MAXDATASIZE];
			//	int outlen, tmplen;
			//	unsigned char outbuf[1024];
unsigned char key_material[256];
unsigned char keymaterial[256];

unsigned char *key;
	unsigned char *iv;
    unsigned char inbuf[256];
//	unsigned char outbuf[1024];
	int outlen,tmplen,i;
	int n;
	int ic = 1000;
	 char *salt = "1234";
	 unsigned char outbuf[2*MAXDATASIZE];
	 
unsigned	char *outf=(unsigned char *)malloc(2*MAXDATASIZE*sizeof(char));


			int t=0,numbytes;
			int no=0,th;
			list[cur].test=0;
			//head=append(&head, argu[2],z,x);
		list[cur].flag=0;
			char bufsend[2*MAXDATASIZE];
		while(1)
			{
									list[cur].test++;
									if ((numbytes = recv(nefd, buf, MAXDATASIZE-1, 0)) == -1)
									{
												perror("recv");
												exit(1);
									}

								buf[numbytes] = '\0';
										if(list[cur].test ==1)
										{
							strcpy(list[cur].name,buf);
										}
												if(list[cur].test ==2)
										{
						char *ch=		search(head,list[cur].name,buf);//get in your list[cur] passkey from DB
									//cout<<ch;
											if(ch[0]!=' ')
									     {                      //check whether the stuff matched or not
																strcpy(list[cur].pass,buf);
																//strcpy(list[cur].password,buf);
															  //  std::string md=getmd5(buf);
																PKCS5_PBKDF2_HMAC_SHA1(list[cur].pass, strlen(list[cur].pass), (unsigned char *)salt, strlen(salt), ic, 128, keymaterial);
															   list[cur].passphrase=keymaterial;
																//	  cout<<"md5 is "<<md<<endl;
																//strcpy(list[cur].passkey,md.c_str());//comment out this line
																//cout<<"passkey "<<list[cur].passkey<<endl;
																list[cur].flag=1;

															/*	if (send(nefd,(char *) keymaterial, strlen((char *)keymaterial), 0) == -1)
																						   {
																							//	printf("send in thread \n");
																								perror("send ");
																						   }*/

											}
											else
											{
												//close(list[cur].fdno);
												//pthread_kill(thread1[curnum], 1);
												//close connection
												printf("\n");
												fflush(stdout);
												list[cur].flag=0;
												break;
											//add log
											}
										}																

								

								int mm;
								if(list[cur].test >2&&list[cur].flag==1)
								{
									strcpy(bufsend,list[cur].name);
								strcat(bufsend," : ");

int outle,tmple;
//unsigned	char *outf=(unsigned char *)malloc(MAXDATASIZE*sizeof(char));
EVP_CIPHER_CTX ctx;
								EVP_CIPHER_CTX_init(&ctx);



 EVP_DecryptInit_ex(&ctx,EVP_aes_256_cbc(),NULL,keymaterial,keymaterial+32);

 EVP_DecryptUpdate(&ctx,outf,&outle,(unsigned char *)buf,numbytes);

 EVP_DecryptFinal_ex(&ctx,outf+outle,&tmple);

 outle+=tmple;

 EVP_CIPHER_CTX_cleanup(&ctx);
								//decrypt the buf before concatenating using cur passkey
								//cout<<" outbuf " <<outf<<endl;
								strcat(bufsend,(char *)outf);
								strcpy(bufhold,bufsend);
								//cout<<"buf is "<<bufhold<<endl;
								//char mes[100]="Server got it";
										for (mm=0;mm<=num ;mm++ )
										{
													strcpy(bufsend,bufhold);
													if(mm!=cur&&list[mm].flag==1) 
													{
														//	unsigned char outbuf[2*MAXDATASIZE];
															EVP_CIPHER_CTX ctx;
															PKCS5_PBKDF2_HMAC_SHA1(list[mm].pass, strlen(list[mm].pass), (unsigned char *)salt, strlen(salt), ic, 128, key_material);
																key = key_material;
																
																	iv = key_material + 32;

															EVP_CIPHER_CTX_init(&ctx);
															EVP_EncryptInit_ex(&ctx, EVP_aes_256_cbc(), NULL, key, iv);
															// EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, list[mm].passkey, list[mm].pass);
																



															if(!EVP_EncryptUpdate(&ctx, outbuf, &outlen,(unsigned char *) bufsend, strlen(bufsend)))
																{
																		/* Error */
																		return 0;
																}
																//outlen+=tmplen;

															
															if (!EVP_EncryptFinal_ex(&ctx, outbuf+outlen, &tmplen)) {
		return (0);
	}
	outlen+=tmplen;







 EVP_CIPHER_CTX_cleanup(&ctx);


													//strcat(bufsend, " --using passkey : ");//comment out this
	                                                 //  strcat(bufsend, list[mm].passkey);//encrypt bufsend with passkey using mm passkey
															if (send(list[mm].fdno, outbuf, outlen, 0) == -1)
														   {
															//	printf("send in thread \n");
																perror("send prob ");
														   }

													//	free(outbuf);

												  }
									  }
	//free(outf);

							   }
			//t++;
																															
		 }
																								
							close(nefd);  // parent doesn't need this
					
							
}


std::string getmd5(char *buf)
{
	md5wrapper md5;
	std::string ret="NULL";
	 ret=md5.getHashFromString(buf);
	 return ret;
}
